package com.ld.shieldsb.common.core.util.solr;

import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest.METHOD;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.BaseHttpSolrClient;
import org.apache.solr.client.solrj.request.CoreAdminRequest;
import org.apache.solr.client.solrj.request.LukeRequest;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.client.solrj.response.LukeResponse;
import org.apache.solr.client.solrj.response.LukeResponse.FieldInfo;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.CoreAdminParams.CoreAdminAction;
import org.apache.solr.common.util.NamedList;

import com.ld.shieldsb.common.core.model.PropertiesModel;
import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.reflect.ModelUtil;
import com.ld.shieldsb.common.core.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * solr操作工具类
 * 
 * @ClassName SolrUtil
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2021年12月3日 下午3:53:02
 *
 */
@Slf4j
public class SolrUtil {
    private SolrUtil() {
        // 私有类，不允许构造;
    }

    public static final String CONFIG_KEY_SOLR_USEPWD = "solr_use_password";
    public static final String CONFIG_KEY_SOLR_USERNAME = "solrUserName";
    public static final String CONFIG_KEY_SOLR_PASSWORD = "solrPassword";

    // 全量导入
    public static final String FULL_IMPORT_URL_PARAMS = "command=full-import&commit=true&wt=json&indent=true&verbose=false"
            + "&optimize=false&debug=false";
    // 增量更新
    public static final String UPDATE_URL_PARAMS = "command=delta-import&commit=true&wt=json&indent=true&verbose=false"
            + "&clean=false&optimize=false&debug=false";
    // 优化url参数
    public static final String OPTIMIZE_URL_PARAMS = "optimize=true";

    /**
     * 获取无密码客户端
     * 
     * @Title getSolrClient
     * @author 吕凯
     * @date 2021年12月3日 下午3:52:45
     * @param baseUrl
     * @return SolrClient
     */
    public static SolrClient getSolrClient(String baseUrl) {
        return getSolrClient(baseUrl, null, null);
    }

    /**
     * 获取带密码客户端
     * 
     * @Title getSolrClientWithPassword
     * @author 吕凯
     * @date 2021年12月3日 下午3:52:30
     * @param baseUrl
     * @return SolrClient
     */
    public static SolrClient getSolrClientWithPassword(String baseUrl) {
        return getSolrClient(baseUrl, PropertiesModel.CONFIG.getString(CONFIG_KEY_SOLR_USERNAME),
                PropertiesModel.CONFIG.getString(CONFIG_KEY_SOLR_PASSWORD));
    }

    /**
     * 获取SolrClient
     * 
     * @Title getSolrClient
     * @author 吕凯
     * @date 2021年12月3日 下午3:50:33
     * @param baseUrl
     * @param userName
     * @param password
     * @return SolrClient
     */
    public static SolrClient getSolrClient(String baseUrl, String userName, String password) {
        return SolrAuthClient.build(baseUrl, userName, password);
    }

    /**
     * 返回字段信息
     * 
     * @Title getFieldInfo
     * @author 吕凯
     * @date 2021年12月22日 上午9:37:19
     * @param baseUrl
     * @param userName
     * @param password
     * @return
     * @throws SolrServerException
     * @throws IOException
     *             Map<String,FieldInfo>
     */
    public static Map<String, FieldInfo> getFieldInfo(String baseUrl, String userName, String password)
            throws SolrServerException, IOException {
        return getFieldInfo(SolrUtil.getSolrClient(baseUrl, userName, password));
    }

    /**
     * 返回字段信息
     * 
     * @Title getFieldInfo
     * @author 吕凯
     * @date 2021年12月22日 上午9:37:29
     * @param solrClient
     * @return
     * @throws SolrServerException
     * @throws IOException
     *             Map<String,FieldInfo>
     */
    public static Map<String, FieldInfo> getFieldInfo(SolrClient solrClient) throws SolrServerException, IOException {
        LukeRequest luke = new LukeRequest();
        luke.setShowSchema(false);
        LukeResponse process = luke.process(solrClient);
        return process.getFieldInfo();
    }

    /**
     * 
     * 获取UpdateRequest，添加密码后无法使用SolrClient，所以需要用到UpdateRequest，用于更新
     * 
     * @Title getSolrUpdateRequest
     * @author 张和祥
     * @date 2021年4月13日 11:07:46
     * @return UpdateRequest
     */
    public static UpdateRequest getSolrUpdateRequest() {
        return getSolrUpdateRequest(null, null);
    }

    public static UpdateRequest getSolrUpdateRequest(String userName, String password) {
        UpdateRequest updateRequest = new UpdateRequest();
        if (StringUtils.isNotEmpty(userName) && StringUtils.isNotEmpty(password)) {
            updateRequest.setBasicAuthCredentials(userName, password);
        }
        return updateRequest;
    }

    public static boolean add(SolrClient solrClient, Object model) {
        return add(solrClient, model, true);
    }

    /**
     * 添加model
     * 
     * @Title addModel
     * @author 吕凯
     * @date 2021年12月3日 下午6:05:12
     * @param solrClient
     * @param model
     * @return boolean
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static boolean add(SolrClient solrClient, Object model, boolean autoCommit) {
        if (model != null) {// 更新操作
            SolrInputDocument document = new SolrInputDocument();
            Map<String, Object> map = null;
            if (model instanceof Map) {
                map = (Map) model;
            } else {
                map = ModelUtil.model2map(model);
            }
            log.warn(map.toString());
            for (Entry<String, Object> e : map.entrySet()) {
                document.addField(e.getKey(), e.getValue());
            }
            // solrClient.add在solr需要账号密码是有问题，此处修改为使用UpdateRequest处理
            UpdateRequest updateRequest;
            if (solrClient instanceof SolrAuthClient) { // 带密码客户端
                SolrAuthClient hsClient = (SolrAuthClient) solrClient;
                updateRequest = SolrUtil.getSolrUpdateRequest(hsClient.getUserName(), hsClient.getPassword());
            } else {
                updateRequest = SolrUtil.getSolrUpdateRequest();
            }
            updateRequest.add(document);
            try {
                updateRequest.process(solrClient); // 提交处理
                // 不自动提交则不提交
                if (autoCommit) {
                    updateRequest.commit(solrClient, null); // 升级solrj后，操作需要commit。collection没有则为null
                }
                return true;
            } catch (SolrServerException | IOException e) {
                log.error("更新SOLR出错！", e);
                return false;
            }
        }
        return false;
    }

    public static boolean commit(SolrClient solrClient) {
        // solrClient.add在solr需要账号密码是有问题，此处修改为使用UpdateRequest处理
        UpdateRequest updateRequest;
        if (solrClient instanceof SolrAuthClient) { // 带密码客户端
            SolrAuthClient hsClient = (SolrAuthClient) solrClient;
            updateRequest = SolrUtil.getSolrUpdateRequest(hsClient.getUserName(), hsClient.getPassword());
        } else {
            updateRequest = SolrUtil.getSolrUpdateRequest();
        }
        try {
            updateRequest.commit(solrClient, null); // 升级solrj后，操作需要commit。collection没有则为null
            return true;
        } catch (SolrServerException | IOException e) {
            log.error("提交SOLR出错！", e);
            return false;
        }
    }

    /**
     * 删除数据
     * 
     * @Title delSolr
     * @author 吕凯
     * @date 2021年12月3日 下午3:19:57
     * @param solrClient
     * @param id
     * @return boolean
     */
    public static boolean delById(SolrClient solrClient, Object id) {
        try {
            solrClient.deleteById(id + "");
        } catch (Exception e) {
            log.error("", e);
            return false;
        }
        return true;
    }

    /**
     * 增加浏览数
     *
     * @param solrClient
     * @param pkValue
     *            主键值
     * @param fieldName
     *            浏览数字段名
     * @param addViewCount
     *            增加的浏览数
     * @return
     * @throws SolrServerException
     * @throws IOException
     *             boolean
     * @Title addSolrViewCount
     * @author 吕凯
     * @date 2016年1月5日 下午4:18:48
     */
    public static boolean addViewCount(SolrClient solrClient, Object pkValue, String fieldName, int addViewCount)
            throws IOException, SolrServerException {
        return addNum(solrClient, "id", pkValue, fieldName, addViewCount);
    }

    /**
     * 增加浏览数
     *
     * @param solrClient
     * @param pkValue
     *            主键值
     * @param addViewCount
     *            增加的浏览数
     * @return
     * @throws SolrServerException
     * @throws IOException
     *             boolean
     * @Title addSolrViewCount
     * @author 吕凯
     * @date 2016年1月5日 下午4:10:30
     */
    public static boolean addViewCount(SolrClient solrClient, Object pkValue, int addViewCount) throws IOException, SolrServerException {
        return addNum(solrClient, "id", pkValue, "viewCount", addViewCount);
    }

    /**
     * 增加浏览数
     *
     * @param solrClient
     * @param pkName
     *            主键名
     * @param pkValue
     *            主键值
     * @param fieldName
     *            浏览数字段名
     * @param addNum
     *            增加浏览数值
     * @return
     * @throws SolrServerException
     * @throws IOException
     *             boolean
     * @Title addSolrViewCount
     * @author 吕凯
     * @date 2016年1月5日 下午4:09:06
     */
    public static boolean addNum(SolrClient solrClient, String pkName, Object pkValue, String fieldName, int addNum)
            throws IOException, SolrServerException {
        HashMap<String, Object> oper = new HashMap<>();
        oper.put("inc", addNum);

        SolrInputDocument doc = new SolrInputDocument();
        doc.addField(pkName, pkValue);
        doc.addField(fieldName, oper);

        solrClient.add(doc);
        return true;
    }

    /**
     * 更新指定字段的值
     *
     * @param solrClient
     * @param pkValue
     *            主键值
     * @param fieldName
     *            更新的字段名
     * @param fieldValue
     *            更新的字段值
     * @return
     * @throws SolrServerException
     * @throws IOException
     *             boolean
     * @Title updateSolr
     * @author 吕凯
     * @date 2016年1月5日 下午3:36:00
     */
    public static boolean update(SolrClient solrClient, Object pkValue, String fieldName, Object fieldValue)
            throws IOException, SolrServerException {
        return update(solrClient, "id", pkValue, fieldName, fieldValue);
    }

    /**
     * 更新指定字段的值
     *
     * @param solrClient
     * @param pkName
     *            主键字段名
     * @param pkValue
     *            主键字段值
     * @param fieldName
     *            更新字段名
     * @param fieldValue
     *            更新字段值
     * @return
     * @throws SolrServerException
     * @throws IOException
     *             boolean
     * @Title updateSolr
     * @author 吕凯
     * @date 2016年1月5日 下午3:37:55
     */
    public static boolean update(SolrClient solrClient, String pkName, Object pkValue, String fieldName, Object fieldValue)
            throws IOException, SolrServerException {
        HashMap<String, Object> oper = new HashMap<>();
        oper.put("set", fieldValue);

        SolrInputDocument doc = new SolrInputDocument();
        doc.addField(pkName, pkValue);
        doc.addField(fieldName, oper);

        solrClient.add(doc);
        // SolrClient.commit();
        return true;
    }

    public static boolean update(SolrClient solrClient, Object model) throws SolrServerException, IOException {
        return update(solrClient, "id", model);
    }

    /**
     * 更新model中存在的值（model内为null或不存在的不更新，add方法如果为null或不存在会移除字段，与更新操作不符合
     *
     * @param solrClient
     * @param pkName
     *            主键名称，如id,companyId等
     * @param model
     * @return
     * @throws SolrServerException
     * @throws IOException
     *             boolean
     * @Title updateSolr
     * @author 吕凯
     * @date 2016年1月5日 下午3:34:49
     */
    @SuppressWarnings("unchecked")
    public static boolean update(SolrClient solrClient, String pkName, Object model) throws IOException, SolrServerException {
        if (pkName == null) {
            pkName = "id";
        }
        if (model != null) {// 更新操作
            SolrInputDocument doc = new SolrInputDocument();
            Map<String, Object> map = null;
            if (model instanceof Map) {
                map = (Map<String, Object>) model;
            } else {
                map = ModelUtil.model2map(model);
            }
            Object id = null;
            for (Entry<String, Object> e : map.entrySet()) {
                if (!pkName.equals(e.getKey())) {
                    HashMap<String, Object> oper = new HashMap<>();
                    Object evalue = e.getValue();
                    if ("".equals(evalue)) {
                        evalue = null;
                    }
                    oper.put("set", evalue);
                    doc.addField(e.getKey(), oper);
                } else {
                    id = e.getValue();
                }
            }
            if (id != null) {
                doc.addField(pkName, id);
                // solrClient.add在solr需要账号密码是有问题，此处修改为使用UpdateRequest处理
                UpdateRequest updateRequest;
                if (solrClient instanceof SolrAuthClient) { // 带密码客户端
                    SolrAuthClient hsClient = (SolrAuthClient) solrClient;
                    updateRequest = SolrUtil.getSolrUpdateRequest(hsClient.getUserName(), hsClient.getPassword());
                } else {
                    updateRequest = SolrUtil.getSolrUpdateRequest();
                }
                updateRequest.add(doc);
                try {
                    updateRequest.process(solrClient); // 提交处理
                    updateRequest.commit(solrClient, null); // 升级solrj后，操作需要commit。collection没有则为null
                    return true;
                } catch (SolrServerException | IOException e) {
                    log.error("更新SOLR出错！", e);
                    return false;
                }
            } else {
                log.warn("id不存在::" + model);
            }

        }
        return false;
    }

    public static <T> PageNavigationBean<T> getPageBean(SolrClient solrClient, SolrQuery query, Class<T> modelType, int pageNum,
            int pageSize) {
        return getPageBean(solrClient, query, null, modelType, pageNum, pageSize);
    }

    /**
     * 返回分页结构
     *
     * @param solrClient
     * @param query
     * @param fieldList
     *            返回的字段列表，以逗号或空格分隔
     * @param modelType
     * @param pageNum
     * @param pageSize
     * @return PageNavigationBean<T>
     * @Title getPageBean
     * @author 吕凯
     * @date 2016年1月5日 下午5:12:40更新
     */
    public static <T> PageNavigationBean<T> getPageBean(SolrClient solrClient, SolrQuery query, String fieldList, Class<T> modelType,
            int pageNum, int pageSize) {
        if (!StringUtils.isBlank(fieldList)) {
            query.add("fl", fieldList);
        }
        PageNavigationBean<T> pageBean = new PageNavigationBean<>();
        pageBean.setPageSize(pageSize);
        pageBean.setCurrentPage(pageNum);
        query.setStart(pageBean.getCurrentPoint() - 1).setRows(pageSize);
        try {
            if (query.getTimeAllowed() == null) {
                query.setTimeAllowed(5000);
            }
            QueryResponse response = solrClient.query(query, METHOD.POST);
            log.info("耗时：" + response.getElapsedTime());
            pageBean.setTotalCount((int) response.getResults().getNumFound());
            for (SolrDocument doc : response.getResults()) {
                Map<String, Object> map = doc.getFieldValueMap();
                T model = ModelUtil.map2model(map, modelType);
                // 高亮
                if (response.getHighlighting() != null) {
                    Object id = doc.getFieldValue("id");
                    for (Entry<String, List<String>> e : response.getHighlighting().get(id).entrySet()) {
                        ModelUtil.setModelValue(model, e.getKey(), e.getValue().get(0));
                    }
                }

                ModelUtil.confirmValues(model);
                pageBean.getResultList().add(model);
            }

        } catch (Exception e) {
            pageBean.setHasError(true);
            log.error("检索失败:" + query.getQuery(), e);
        }
        return pageBean;
    }

    /**
     * 访问url
     * 
     * @Title curl
     * @author 吕凯
     * @date 2021年12月3日 下午3:07:43
     * @param url
     * @throws IOException
     *             void
     */
    public static void openUrl(String url) throws IOException {
        new URL(url).openStream().close();
    }

    /**
     * 去掉字符串开头的标点符号(带&的默认为转义字符，不作处理)
     *
     * @param value
     * @return
     * @author zhhx
     */
    public static String removeBd4Start(String value) {
        if (StringUtils.isEmpty(value)) {
            return "";
        }
        value = value.trim().replaceAll("&nbsp;", "").replaceAll("　", "");
        // 把高亮样式先转换，避免错误判断
        value = value.replace("<span class='archivesHighlighting'>", "archivesHighlightingTagStart").replace("</span>",
                "archivesHighlightingTagEnd");
        String regex = "[\\pP\\p{Punct}]"; // 标点符号正则表达式
        int i = 0;
        String[] strs = value.split(regex);
        // 判断是否以标点符号开头(去掉转义字符)
        if (!value.startsWith("&") && strs[0].length() == 0) {
            // 如果以标点符号开头，则去掉标点符号
            for (String str : strs) {
                if ("".equals(str)) {
                    i++;
                } else {
                    break;
                }
            }
            value = value.substring(i);
        }
        return value.replace("archivesHighlightingTagStart", "<span class='archivesHighlighting'>").replace("archivesHighlightingTagEnd",
                "</span>");
    }

    public static boolean addSolr(SolrClient solrClient, Map<String, Object> map) {
        if (map != null) {// 更新操作
            SolrInputDocument document = new SolrInputDocument();
            for (Entry<String, Object> e : map.entrySet()) {
                document.addField(e.getKey(), e.getValue());
            }
            // solrClient.add在solr需要账号密码是有问题，此处修改为使用UpdateRequest处理
            UpdateRequest updateRequest = SolrUtil.getSolrUpdateRequest();
            updateRequest.add(document);
            try {
                updateRequest.process(solrClient); // 提交处理
                updateRequest.commit(solrClient, null); // 升级solrj后，操作需要commit。collection没有则为null
                return true;
            } catch (SolrServerException | IOException e) {
                log.error("更新SOLR出错！", e);
                return false;
            }
        }
        return false;
    }

    /**
     * 增量更新索引，注意需要提前配置好db-data-config.xml文件
     * 
     * @Title updateEngine
     * @author 吕凯
     * @date 2021年12月3日 下午3:06:40
     * @param indexName
     *            void
     */
    public static void updateEngine(String indexName) {
        try {
            openUrl(indexName + "/dataimport?" + UPDATE_URL_PARAMS);
            log.info("全文索引[{}]增量更新请求提交完成", indexName);
        } catch (Exception e) {
            log.error("全文索引[" + indexName + "]增量更新请求提交失败", e);
        }
    }

    /**
     * 全量导入，注意需要提前配置好db-data-config.xml文件
     * 
     * @Title fullImportEngine
     * @author 吕凯
     * @date 2021年12月3日 下午3:15:23
     * @param indexName
     * @param clean
     *            void
     */
    public static void fullImportEngine(String indexName, boolean clean) {
        try {
            openUrl(indexName + "/dataimport?" + FULL_IMPORT_URL_PARAMS + "&clean=" + clean);
            log.info("全文索引[{}]全量导入请求提交完成", indexName);
        } catch (Exception e) {
            log.error("全文索引[" + indexName + "]全量导入请求提交失败", e);
        }
    }

    /**
     * 优化索引
     * 
     * @Title optimizeEngine
     * @author 吕凯
     * @date 2021年12月3日 下午3:07:01
     * @param indexName
     *            void
     */
    public static void optimizeEngine(String indexName) {
        try {
            openUrl(indexName + "/update?" + OPTIMIZE_URL_PARAMS);
            log.info("全文索引[{}]优化请求提交完成", indexName);
        } catch (Exception e) {
            log.error("全文索引[" + indexName + "]优化请求提交失败", e);
        }
    }

    /**
     * 
     * 获取核信息
     * 
     * @date 2021年12月22日 下午3:12:50
     * @Title getCores
     * @author 吕凯
     * @date 2021年12月22日 下午3:12:50
     * @param server
     * @param coreName
     *            核名称，不填搜全部
     * @return
     * @throws SolrServerException
     * @throws IOException
     *             Map<String,NamedList<Object>>
     */
    public static Map<String, NamedList<Object>> getCores(SolrClient server, String coreName) throws SolrServerException, IOException {
        return getCores(server, false, coreName);
    }

    /**
     * 获取核信息
     * 
     * @Title getCoreStatus
     * @author 吕凯
     * @date 2021年12月22日 下午3:12:37
     * @param server
     * @param isIndexInfoNeeded
     *            索引信息
     * @return
     * @throws SolrServerException
     * @throws IOException
     *             NamedList<NamedList<Object>>
     */
    public static Map<String, NamedList<Object>> getCores(SolrClient server, boolean isIndexInfoNeeded, String coreName)
            throws SolrServerException, IOException {
        CoreAdminRequest req = new CoreAdminRequest();
        req.setAction(CoreAdminAction.STATUS);
        req.setIndexInfoNeeded(isIndexInfoNeeded);
        Map<String, NamedList<Object>> dataMap = new LinkedHashMap<>();
        if (StringUtils.isEmpty(coreName)) { // 选择全部

            NamedList<NamedList<Object>> cores = req.process(server).getCoreStatus();
            Iterator<Map.Entry<String, NamedList<Object>>> it = cores.iterator();
            while (it.hasNext()) {
                Map.Entry<String, NamedList<Object>> next = it.next();
                dataMap.put(next.getKey(), next.getValue());
            }
        } else { // 根据corename筛选
            NamedList<Object> cores = req.process(server).getCoreStatus(coreName);
            dataMap.put(coreName, cores);
        }
        return dataMap;
    }

    /**
     * 测试连接
     * 
     * @Title testConn
     * @author 吕凯
     * @date 2022年1月4日 下午1:52:13
     * @param server
     * @return Result
     */
    public static Result testConn(SolrClient server) {
        Result result = new Result();
        String msg = "";
        try {
            server.ping(); // 能ping通说明正常,直接到core的可以ping通，到根目录的不行如：http://192.168.1.247:8983/rlos
        } catch (BaseHttpSolrClient.RemoteSolrException e) { //
            log.warn("solr出错！", e);
            msg = e.getMessage(); // 404
            try {
                // 获取核成功说明测试成功
                SolrUtil.getCores(server, null);
                result.setSuccess(true); // 走到这一步说明上面没有问题
                result.setMessage("连接正常");
            } catch (BaseHttpSolrClient.RemoteSolrException e1) {
                log.warn("solr出错！", e1);
                int code = e1.code();
                if (code == 401) {
                    msg = "验证未通过！";
                }
            } catch (Exception e1) {
                log.warn("solr出错！", e1);
                msg = e1.getMessage();
            }
        } catch (Exception e) {
            log.warn("solr出错！", e);
            msg = e.getMessage();
        }
        result.setMessage(msg);
        return result;
    }

    public static Result testConn(String baseUrl, String userName, String password) {
        SolrClient server = SolrUtil.getSolrClient(baseUrl, userName, password);
        return testConn(server); // 能ping通说明正常,直接到core的可以ping通，到根目录的不行如：http://192.168.1.247:8983/rlos
    }

    public static void main(String[] args) throws SolrServerException, IOException {
        SolrClient server = SolrUtil.getSolrClient("http://192.168.1.247:8983/rlos/baoding_icpjiben", "admin", "11235813Admin@");
        System.out.println(getFieldInfo(server));
        // CoreAdminRequest //操作core
        /* SolrClient server = SolrUtil.getSolrClient("http://192.168.1.247:8983/rlos", "admin", "11235813Admin@");
        //        SolrPingResponse respnese = server.ping();
        //        System.out.println(respnese);
        Map<String, NamedList<Object>> cores = getCores(server, null);
        System.out.println(cores.size());
        for (Map.Entry<String, NamedList<Object>> entry : cores.entrySet()) {
            System.out.println(entry.getKey() + "===" + entry.getValue());
        }*/
    }
}